package fr.lille.inria.adam.lifl.EcoGardeInventory.scrapper;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.message.BasicNameValuePair;

import android.util.Log;
import fr.lille.inria.adam.lifl.EcoGardeInventory.Utils.LambertConformalConicProjection;
import fr.lille.inria.adam.lifl.EcoGardeInventory.Utils.SIGObservation;

/**
 * Cette t?che est responsable du formatage et de l'envoie d'une observation sur la serveur 
 * SIG-Nature. 
 */
public class SendObservationTask extends HttpAsyncTask<SIGObservation, String> {
	private static final String TAG = "SendObservationTask";
	protected final static String observationPostUrl = "http://signature.enm-lille.fr/enlmsig/index.php?cont=mod_bio_res&event=addBio";
	protected final static String observationPostFilesUrl = "http://signature.enm-lille.fr/enlmsig/index.php?cont=mod_bio_res&event=upload_files";
	
	@Override
	protected String doInBackground(SIGObservation... params) {
		/* On va envoyer un forumulaire au serveur avec
		 * l'observation dedans. Si l'observation inclus des photos
		 * alors ces photos seront envoyées dans un second temps. */
		SIGObservation n = params[0];
		Log.d(TAG, n.toString());
		
		// Envoi du formlaire
    	// Exemple de réponse (succes): {"msg":"Entr\u00e9e ajout\u00e9e avec succ\u00e8s","succes":1}
		final HttpPost observationPost = buildObservationPost(n);
    	HttpResponse response = browser.execute(observationPost);
    	final String observationPostResponse = super.getStringContent(response);
    	
    	
    	// Une fois l'observation envoy?e avec succ?s,
        // nous devons envoyer les ?ventuels fichiers compl?mentaire
    	try {
	    	final File photoFile = getPhotoFile(n);
	    	if (photoFile != null) {
	    		final HttpPost observationFilesPost = buildPhotoPost(photoFile);
	        	browser.execute(observationFilesPost); // On envoie
	        	Log.d(TAG, "Photo envoyée");
	    	}
    	} catch (IOException e) {
			Log.wtf(TAG, "Problème IO lors de l'envoi de la photo!",e);
		}
    	
        return observationPostResponse;
	}
	
	/**Construit la requête au serveur pour l'envoi d'un fichier photo. Pour 
	 * que l'envoi fonctionne il faut que le nom du fichier soit le même
	 * @param photoFile
	 * @return
	 * @throws IOException
	 */
	private static HttpPost buildPhotoPost(File photoFile) throws IOException {
		final byte[] photoBytes = new byte[(int) photoFile.length()];
		final FileInputStream reader = new FileInputStream(photoFile);
		reader.read(photoBytes);
		reader.close();
		
		final HttpPost observationFilesPost = new HttpPost(observationPostFilesUrl);
		final MultipartEntity fileUploadEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
    	fileUploadEntity.addPart("upload_file[]", new ByteArrayBody(photoBytes, photoFile.getName()));
    	fileUploadEntity.addPart("upload_file[]", new ByteArrayBody(new byte[]{}, ""));
    	observationFilesPost.setEntity(fileUploadEntity);
		
		return observationFilesPost;
	}
	
	private static HttpPost buildObservationPost(SIGObservation obs) {
		final HttpPost observationPost = new HttpPost(observationPostUrl);
        observationPost.addHeader("X-Requested-With", "XMLHttpRequest");
		
        final List<NameValuePair> postForm = new ArrayList<NameValuePair>(18);
		postForm.add(new BasicNameValuePair("site", ""));
        postForm.add(new BasicNameValuePair("ueg", ""));
        postForm.add(new BasicNameValuePair("recensement", Integer.toString(obs.recensement)));
        Calendar obsTimeStamp = Calendar.getInstance();
        obsTimeStamp.setTimeInMillis(obs.timestamp);
        final String rightNowString = String.format("%d-%d-%d", 
        		obsTimeStamp.get(Calendar.DAY_OF_MONTH),
        		obsTimeStamp.get(Calendar.MONTH)+1, //Les mois commencent à zéro
        		obsTimeStamp.get(Calendar.YEAR));
        postForm.add(new BasicNameValuePair("start_date", rightNowString));
        postForm.add(new BasicNameValuePair("end_date", rightNowString));
        postForm.add(new BasicNameValuePair("groupe", Integer.toString(obs.groupeEspece)));
        postForm.add(new BasicNameValuePair("insee", ""));
        
        // Conversion coordonnées GPS au format du SIG (WGS84 -> RGF93)
        double[] lambertCoords = null;
        if (obs.location.hasAltitude()) lambertCoords=LambertConformalConicProjection.WGS84toLambert93(obs.location.getLatitude(), obs.location.getLongitude(), obs.location.getAltitude());
        else lambertCoords=LambertConformalConicProjection.WGS84toLambert93(obs.location.getLatitude(), obs.location.getLongitude());
        postForm.add(new BasicNameValuePair("x", Double.toString(lambertCoords[1])));
        postForm.add(new BasicNameValuePair("y", Double.toString(lambertCoords[0])));
        postForm.add(new BasicNameValuePair("type_donnee", "terrain"));
        postForm.add(new BasicNameValuePair("notes", obs.noteTextuelle));
        postForm.add(new BasicNameValuePair("arrObs", Integer.toString(obs.observateur)));
        postForm.add(new BasicNameValuePair("dataEspeces", String.format(
        		"%d|%d|%d|%s|%d", 
        		obs.nomEspece, obs.populationSize, obs.populationSizePrecision, obs.populationGender.code, obs.comportement)));
        postForm.add(new BasicNameValuePair("infosComp", "param55=|param59=|param53="));
        postForm.add(new BasicNameValuePair("fichier", ""));
        final File photoFile = getPhotoFile(obs);
        postForm.add(new BasicNameValuePair("photo", (photoFile==null)?"":photoFile.getName()));
        
		try {
			final HttpEntity e = new UrlEncodedFormEntity(postForm);
			observationPost.setEntity(e);
		} catch (UnsupportedEncodingException e1) {
			Log.wtf(TAG, "Les données dans l'observation ne peuvent être envoyées par formulaire! " +
					"Poursuite avec observation vide", e1);
		}
        
        return observationPost;
	}
	
	/**S'occupe des vérifications et des rapports de bugs 
	 * @param o L'observation
	 * @return Retourne le fichier photo s'il existe. null sinon
	 */
	private static File getPhotoFile(SIGObservation obs) {
		try {
        	// Ajout d'une photo si elle existe
        	if (obs.photoUri!=null && !obs.photoUri.isEmpty()) {
        		final URI photoPath = new URI(obs.photoUri);
        		final File photoFile = new File(photoPath);
        		
        		if (photoFile.exists() && photoFile.isFile()) return photoFile;
        		else throw new FileNotFoundException();
        	} else return null;
		} catch (URISyntaxException e2) {
			Log.wtf(TAG, "L'emplacement de la photo n'est pas une URI. La photo ne sera pas envoyée", e2);
		} catch (FileNotFoundException e) {
			Log.wtf(TAG, "La photo à envoyer n'est pas présente en mémoire", e);
		}
		
		return null;
	}
}
